#ifndef __SLOTHTTPD_CACHED_DESCRIPTOR_HPP__
#define __SLOTHTTPD_CACHED_DESCRIPTOR_HPP__

#include <slothttpd/generic_pool.hpp>

#include <ctime>

#include <sys/stat.h>

#include <string>

#include <boost/intrusive_ptr.hpp>
#include <boost/intrusive/splay_set.hpp>

namespace slothttpd {

class file;
typedef boost::intrusive_ptr<file> file_ptr;

class file :
        public boost::intrusive::splay_set_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink> >
{
    friend void intrusive_ptr_add_ref(file *r) noexcept;
    friend void intrusive_ptr_release(file *r) noexcept;
public:
    enum open_result {
        success = 0,
        not_found,
        forbidden,
        unknown_error
    };
    
    file();
    
    static open_result get_direct_io(generic_pool &pool, const char *path, file_ptr &ref);
    static open_result get_cached(const char *path, file_ptr &ref);
    static open_result get_nocache(generic_pool &pool, const char *path, file_ptr &ref);
    
    inline int descriptor() const noexcept { return fd_; }
    inline std::time_t last_modified() const noexcept { return last_mod_; }
    inline off64_t size() const noexcept { return size_; }
    
    friend inline bool operator == (const file &a, const file &b) { return a.path_ == b.path_; }
    friend inline bool operator < (const file &a, const file &b) { return a.path_ < b.path_; }
    friend inline bool operator > (const file &a, const file &b) { return a.path_ > b.path_; }
    friend inline bool operator != (const file &a, const file &b) { return a.path_ != b.path_; }
    
protected:
    static file_ptr from_pool(generic_pool &pool);
    open_result open(const char *path, int flags);
    void close();
    
protected:
    std::string path_;
    
    // use wrapper for posix descriptor
    int fd_;
    off64_t     size_;
    std::time_t last_mod_;
    
private:
    typedef boost::intrusive::splay_set<file, boost::intrusive::constant_time_size<false> > cache_t;
    
    unsigned long ref_count_;
    bool from_pool_;
    
    static cache_t cache;
};

}

#endif
